#include "CanReader.h"
#include "CanReaper.h"
#include "LoggerConfigs.h"
#include "utils.hpp"

#include <utility>
#include <format>

namespace canpp {
    using std::format;
    using std::vformat;
    using std::format_args;
    using std::make_format_args;


    CanReader::~CanReader() {
        CanReaper::getInstance().removeReader(this);
        logger.force_flush();
    }

    CanReader::CanReader(string &&canInterface, optional<string> &&id,
                         optional<set<uint16_t> > &&canMasks): logger([&] {
        const string real_id = id.value_or(std::to_string(random_number(0, 2000)));
        string LOG_DIR = string(CANPP_LOG_DIR) +
                         "/CanReaders/CanReader_" + real_id;
        const string LOGGER_NAME = "CanReader_" + real_id;
        const string log_config = vformat(readerLoggerConfig
                                          , format_args(make_format_args(LOG_DIR)));
        return log::create_log(LOGGER_NAME, log_config);
    }()), interface(
        std::move(canInterface)), id(id.value_or(std::to_string(random_number(0, 2000)))) {
        this->masks = canMasks ? std::move(*canMasks) : set<uint16_t>{};
        CanReaper::getInstance().bindReader(this, interface);
        logger.info(format("CanReader: {} initialized on {}!", this->id,
                           this->interface));
    }

    CanReader::CanReader(string &canInterface, const optional<string> &id,
                         const optional<set<uint16_t> > &canMasks): CanReader(
        std::move(string(canInterface)), std::move(optional(id)), std::move(optional(canMasks))) {
    }

    can_frame CanReader::peekMessage() {
        return *messages.peek();
    }


    void CanReader::pushMessage(const can_frame &&frame) {
        if (masks.contains(frame.can_id)) {
            messages.insert(frame);
            logger.info(format("Receving frame: {}", format_frame_data(frame)));
            callback_lock.try_lock();
            callback(std::move(frame));
            callback_lock.unlock();
        }
    }

    void CanReader::registerCallback(const std::function<void(const can_frame &&)> &func) {
        callback_lock.try_lock();
        callback = func;
        callback_lock.unlock();
    }

    void CanReader::registerCallback(std::function<void(const can_frame &&)> &&func) {
        callback_lock.try_lock();
        callback = std::move(func);
        callback_lock.unlock();
    }
} // canpp

